package com.felix.spider.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.felix.spider.domain.TrainTimetable;
import com.felix.spider.domain.json.TrainListDO;
import com.felix.spider.domain.json.TrainTimeDO;
import com.felix.spider.rep.TrainTimetableRepository;
import com.felix.spider.utils.HtmlUnitUtils;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;

/**
 * @author felix.ma
 * @create 2018-07-02 14:11
 **/
@Service
public class TrainTimeService {
    @Value("${spider.net.train_list_url}")
    String train_list_url;
    CountDownLatch countDownLatch = new CountDownLatch(7);
    @Value("${spider.file.basedir}")
    private String fileDir;

    @Autowired
    private TrainTimetableRepository trainTimetableRepository;

    private TrainListDO o = null;

    private Thread[] threads;

    public void threadsStop() {
        if (threads != null) {
            for (Thread t : threads) {
                if (!t.isInterrupted()) {
                    t.interrupt();
                }
            }
        }
    }

    public String htmlUnitGet(String startDate) {
//        TODO 线程重启
        threadsStop();
        long sysStartTime = System.currentTimeMillis();
        new File(fileDir).mkdirs();
        File train_list_bak = new File(fileDir, "train_list.js");
        if (!train_list_bak.exists()) {
            try {
                train_list_bak.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String nowDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String text = null;
        try {
            text = FileUtils.readFileToString(train_list_bak, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        String json = text.substring(text.indexOf("=") + 1);
        System.out.println("开始解析数据");
        JSONObject jsonObject;
        if (json.length() > 0) {
            try {
                System.out.println("解析本地数据");
                jsonObject = JSON.parseObject(json);
                System.out.println("本地数据解析成功");
            } catch (Exception e) {
                // 本地解析失败从网络获取
                System.out.println("本地数据解析失败，请求网络数据");
                jsonObject = getNetData(train_list_bak);
            }
        } else {
            System.out.println("本地数据解析失败，请求网络数据");
            jsonObject = getNetData(train_list_bak);
        }
        o = jsonObject.getObject(nowDate, TrainListDO.class);
        if (o == null) {
            System.out.println("本地数据解析失败，请求网络数据");
            jsonObject = getNetData(train_list_bak);
            o = jsonObject.getObject(nowDate, TrainListDO.class);
            if (o == null)
                return nowDate + "数据获取失败<br> 已有日期：<br>" + StringUtils.join(jsonObject.keySet(), "<br>");
        }
        Set<String> strings = jsonObject.keySet();
        System.out.println("数据解析成功，共 " + strings.size() + " 天数据:\n" + StringUtils.join(jsonObject.keySet(), ":"));
        boolean iscontinue = false;
        if (!StringUtils.isEmpty(startDate)) {
            iscontinue = true;
            System.out.println("从" + startDate + "开始遍历");
        }
        Iterator<String> it = strings.iterator();
        while (it.hasNext()) {
            String datestring = it.next();
            if (datestring.equals(startDate)) {
                iscontinue = false;
            }
            if (iscontinue) {
                continue;
            }
            o = jsonObject.getObject(datestring, TrainListDO.class);
            // 获取列车时刻表信息
            System.out.println(datestring + " 开始遍历");
            ArrayList<List<TrainListDO.TrainBeanData>> datas = new ArrayList<>();
            datas.add(o.getC());
            datas.add(o.getD());
            datas.add(o.getG());
            datas.add(o.getK());
            datas.add(o.getO());
            datas.add(o.getT());
            datas.add(o.getZ());
            threads = new Thread[datas.size()];
            countDownLatch = new CountDownLatch(datas.size());
            for (int i = 0; i < datas.size(); i++) {
                List<TrainListDO.TrainBeanData> data = datas.get(i);
                if (data == null) {
                    System.err.println(datestring + "数据为空！！");
                    countDownLatch.countDown();
                    continue;
                }
                int finalI = i;
                Thread thread = new Thread(() -> {
                    for (int j = 0; j < data.size(); j++) {
                        // 使用中断机制，来终止线程
                        if (Thread.currentThread().isInterrupted()) {
                            System.out.println(finalI + "终止");
                            return;
                        }
                        TrainListDO.TrainBeanData trainBeanData = data.get(j);
                        String train_no = trainBeanData.getTrain_no();
                        getTrainTime(datestring, train_no, trainBeanData.getStation_train_code().substring(0, trainBeanData.getStation_train_code().indexOf("(")));
                    }
                    System.out.println(datestring + " " + finalI + "：已完成 " + data.size() + "个");
                    countDownLatch.countDown();
                });
                threads[i] = thread;
                thread.start();
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long sysEndTime = System.currentTimeMillis();
        System.out.println("程序执行时间：" + (sysEndTime - sysStartTime) / 1000 + "秒");
        return "程序执行时间：" + (sysEndTime - sysStartTime) / 1000 + "秒<br>" +
                startDate + "运行结束<br> 已有日期：<br>" + StringUtils.join(jsonObject.keySet(), "<br>");
    }

    private JSONObject getNetData(File train_list_bak) {
        JSONObject result = null;
        try {
            System.out.println("getNetData 方法开始执行 请求网络获取数据");
            long funStartTime = System.currentTimeMillis();
            String text;
            String json;
            WebClient client = HtmlUnitUtils.getClient();
            HtmlPage page = client.getPage(train_list_url);
            client.waitForBackgroundJavaScript(60000);
            //      18547225
            text = page.asText();
            HtmlUnitUtils.close();
            // 获取跟目录
            FileUtils.write(train_list_bak, text, "UTF-8", false);
            json = text.substring(text.indexOf("=") + 1);
            result = JSON.parseObject(json);
            long funEndTime = System.currentTimeMillis();
            System.out.println("getNetData 方法执行时间：" + (funEndTime - funStartTime) / 1000 + "秒");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    private boolean isSaveDataBase(String station_code) {
        String jql = "SELECT count(*) FROM mpi_train_timetable WHERE train_number = :trainNumber";
        Map<String, Object> params = new HashMap<>();
        params.put("trainNumber", station_code);
        Object count = trainTimetableRepository.findSingleObjectBySql(jql, params);
        if (count != null && Integer.valueOf(count.toString()) > 0) {
            return true;
        }
        return false;
    }

    @Transactional
    private void getTrainTime(String nowDate, String train_no, String station_code) {
        if (isSaveDataBase(station_code)) {
            return;
        }
        System.out.println(nowDate + "开始获取列车时刻表 " + train_no + " station_code " + station_code);
        SimpleDateFormat dfHm = new SimpleDateFormat("HH:mm");
        String timeurl =
                "https://kyfw.12306.cn/otn/czxx/queryByTrainNo?train_no="
                        + train_no
                        + "&from_station_telecode=BJP&to_station_telecode=KMM&depart_date="
                        + nowDate;
        Document document = null;
        try {
            document = Jsoup.connect(timeurl).ignoreContentType(true).get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        TrainTimeDO trainTime = JSON.parseObject(document.text(), TrainTimeDO.class);
        List<TrainTimeDO.DataBeanX.DataBean> beans = trainTime.getData().getData();
        System.out.println("开始存入数据库");
        String perStartTime = "";
        String walkTime = "00:00";
//        获取第一个
        if (beans.size() > 0) {
            TrainTimeDO.DataBeanX.DataBean dataBean = beans.get(0);
            TrainTimetable trainTimetable = new TrainTimetable();
            trainTimetable.setTrainNumber(station_code);
            trainTimetable.setSno(dataBean.getStation_no());
            trainTimetable.setStartTime(dataBean.getStart_time());
            trainTimetable.setArrivalTime(dataBean.getArrive_time());
            trainTimetable.setStationName(dataBean.getStation_name());
            trainTimetable.setWalkTime(walkTime);
            // TODO 存入数据库
            trainTimetableRepository.save(trainTimetable);
            perStartTime = dataBean.getStart_time();
        }
        for (int i = 1; i < beans.size(); i++) {
            TrainTimeDO.DataBeanX.DataBean dataBean = beans.get(i);
            TrainTimetable trainTimetable = new TrainTimetable();
            trainTimetable.setTrainNumber(station_code);
            trainTimetable.setSno(dataBean.getStation_no());
            trainTimetable.setStartTime(dataBean.getStart_time());
            trainTimetable.setArrivalTime(dataBean.getArrive_time());
            trainTimetable.setStationName(dataBean.getStation_name());
            //TODO 计算运行时间
            try {
                Date h24 = dfHm.parse("24:00");
                Date h00 = dfHm.parse("00:00");
                Date perTime = dfHm.parse(perStartTime);
                Date endTime = dfHm.parse(dataBean.getArrive_time());
                Date walk = dfHm.parse(walkTime);
                if (endTime.after(perTime)) {
                    long minute = (walk.getTime() - h00.getTime() + endTime.getTime() - perTime.getTime()) / 60000;
                    walkTime = (minute / 60) + ":" + (minute % 60);
                    trainTimetable.setWalkTime(walkTime);
                } else {
                    long minute = (h24.getTime() - perTime.getTime()) / 60000;
                    long m2 = (walk.getTime() - h00.getTime() + endTime.getTime() - h00.getTime()) / 60000;
                    minute += m2;
                    walkTime = (minute / 60) + ":" + (minute % 60);
                    trainTimetable.setWalkTime(walkTime);
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
            // TODO 存入数据库
            trainTimetableRepository.save(trainTimetable);
            perStartTime = dataBean.getStart_time();
        }
        System.out.println(train_no + "存入数据库成功");
    }

    public void trainGet() throws IOException {
        String train_list =
                "https://kyfw.12306.cn/otn/resources/js/query/train_list.js?scriptVersion=1.952";
        Document document = Jsoup.connect(train_list).timeout(100000).get();
        String text = document.text();
        System.out.println(text.length());
        //      900582
        String json = text.substring(text.indexOf("=") + 1);
        JSONObject object = JSON.parseObject(json);
        Object o = object.get("2018-07-17");
        System.out.println(o.toString());
    }
}
